//
// Created by tang on 2022/1/11.
//

#include "app_config.hpp"
#include "data/app_config.hpp"
#include "json/json.hpp"
#include "utils/path_utils.hpp"
#include "event/events.hpp"

#include <optional>
#include <cassert>
#include <fstream>

#include "app/app.hpp"
#include "app/app_base.hpp"

namespace Config
{
    struct ConfigKeyInfo
    {
        const char* const key_str;
        const char* const def_value;
    };

    const ConfigKeyInfo KEY_INFOS[] = {
        {"redmine_server_url","http://zymc:3000/"},
        {"redmine_api_key",""},
        {"sql_driver_name",""},
        {"enable_v_sync","1"},
        {"frame_rate_limit", FRAME_RATE_LIMIT24},
        {"default_item_name","新建事项"},
        {"show_closed_issues", ""},
        {"use_date_editor_only", "1"},
        {"daily_total_work_hours", "8"},
        {"diary_display_by_tab",""},
        {"diary_display_item_time",""},
        {"lunch_break_begin_time","12:00:00"},
        {"lunch_break_end_time","13:00:00"},
        {"get_off_work_time","18:00:00"},
        {"font_size","18"},
        {"style_name","Default"},
        {"window_show_maximize","0"},
        {"statistics_default_use_pie_chart","0"},
        {"upgrade_server_base_url",""},
        {"surveyor_server_en",""},
        {"surveyor_server_ip",""},
    };

    static_assert(std::size(KEY_INFOS) == KEY_COUNT, "Check Key-Str Map");

    struct ConfigData
    {
        std::string config_values[KEY_COUNT];
        ConfigItemPtr root_item;

        void CreateView();
        void LoadToView() const;
        void SaveToFile() const;
    };

    std::string& Value(ConfigKey key)
    {
        assert(key < KEY_COUNT&& key >= 0);
        return AppConfig::Instance().d->config_values[key];
    }

    AppConfig::AppConfig() : d(new ConfigData)
    {
        d->CreateView();
        LoadConfigs();
    }

    AppConfig::~AppConfig()
    {
        SaveConfigs();
        delete d;
    }

    void AppConfig::LoadConfigs() const
    {
        // Apply Default Values
        for (int i = KEY_START; i != KEY_COUNT; ++i)
        {
            d->config_values[i] = KEY_INFOS[i].def_value;
        }

        std::ifstream cfg("config.json"_DP, std::ios::binary | std::ios::in);
        if (!cfg.is_open())
        {
            // skip load
            return;
        }
        auto doc = nlohmann::json::parse(cfg);

        for (int i = KEY_START; i != KEY_COUNT; ++i)
        {
            if (doc.contains(KEY_INFOS[i].key_str))
            {
                // d->config_values[i] = doc[KEY_INFOS[i].key_str].get<std::string>(KEY_INFOS[i].def_value);
                d->config_values[i] = doc.value(KEY_INFOS[i].key_str, KEY_INFOS[i].def_value);
            }
        }
        cfg.close();
    }

    void AppConfig::SaveConfigs() const
    {
        std::ofstream cfg("config.json"_DP, std::ios::binary | std::ios::trunc);
        if (!cfg.is_open())
        {
            // error!!
            abort();
        }

        nlohmann::json root;
        for (int i = KEY_START; i != KEY_COUNT; ++i)
        {
            root[KEY_INFOS[i].key_str] = d->config_values[i];
        }
        cfg << root;
        cfg.close();
    }

    AppConfig& AppConfig::Instance()
    {
        static AppConfig inst;
        return inst;
    }

    std::string AppConfig::Get(ConfigKey key, const std::string& defaultValue) const
    {
        assert(key < KEY_COUNT&& key >= 0);
        return d->config_values[key].empty() ? defaultValue : d->config_values[key];
    }

    std::string AppConfig::Set(ConfigKey key, std::string newValue) const
    {
        assert(key < KEY_COUNT&& key >= 0);
        return (d->config_values[key] = move(newValue));
    }

    void Sync()
    {
        AppConfig::Instance().SaveConfigs();
    }

    void ViewClose(bool is_save)
    {
        if (is_save)
            AppConfig::Instance().d->SaveToFile();
    }

    void RenderConfigurationsView(bool first_frame)
    {
        if (first_frame) // sync values
            AppConfig::Instance().d->LoadToView();

        if (const auto& root = AppConfig::Instance().d->root_item)
            root->Render();
    }

    void ConfigData::CreateView()
    {
        const auto root = new GroupItem{};

        // RedMine
        root->children.emplace_back(
            GroupItem::New("RedMine 设置", {
                               TextItem::New("RedMine服务器地址", &config_values[KEY_REDMINE_SERVER_URL], false,
                                             "如http://zymc:3000/"),
                               TextItem::New("RedMine API Key", &config_values[KEY_REDMINE_API_KEY], false,
                                             "RedMine个人API KEY，在'我的账号' > 'API访问键'里获取"),
                })
                );
        
        // 日记相关
        root->children.emplace_back (GroupItem::New (
                "日记一般设置",
                {
                        TextItem::New ("默认事项名称", &config_values[KEY_DEFAULT_ITEM_NAME], false, "新建事项时的默认名称"),
                        RealItem::New ("每天默认工作时长", &config_values[KEY_DAILY_TOTAL_HOURS], "一天工作小时数"),
                        SwitchItem::New ("显示已关闭的问题", &config_values[KEY_SHOW_CLOSE_ISSUES], "yes", "",
                                         "在日志编辑时，下拉选项中的问题列表显示方式"),
                        SwitchItem::New ("仅使用日期编辑器", &config_values[KEY_USE_DATE_EDITOR_ONLY], "yes", "", "是否允许手动输入日期"),
                        SwitchItem::New ("使用按月的方式来展示日记内容", &config_values[KEY_DISPLAY_USING_TAB], "yes", "", "影响日志列表的展示方式"),
                        SwitchItem::New ("在列表中显示耗时信息", &config_values[KEY_DISPLAY_ITEM_TIME], "yes", "", "额外在列表中展示耗时信息"),
                }));

        // Task Schedule
        root->children.emplace_back(
            GroupItem::New("任务调度计划设置",{
                TimeItem::New ("午休开始时间", &config_values[KEY_LUNCH_BREAK_BEGIN_TIME]),
                TimeItem::New ("午休结束时间", &config_values[KEY_LUNCH_BREAK_END_TIME]),
                TimeItem::New ("下班时间", &config_values[KEY_GET_OFF_WORK_TIME]),
            }, false)
        );

        // Database
        root->children.emplace_back(
            GroupItem::New("数据库设置", {
                               EnumItem::New("选择数据库驱动*", &config_values[KEY_SQL_DRIVER_NAME],
                                             ((Application*)App)->SqlDriverCount(), nullptr,
                                             [](void*, int index) -> const char*
                                             {
                                                 const char* text = ((Application*)App)->SqlDriverName(index);
                                                 return text;
                                             }, "选择使用哪种数据库作为数据储存，需要重新启动，并且会丢失数据！！")
                })
        );

        // Ui
        root->children.emplace_back(
            GroupItem::New("界面设置", {
                               RealItem::New("字体大小", &config_values[KEY_FONT_SIZE], "有效范围为10~32，字体越大内存占用越多。"),
                               FunctionButton::New("应用##Font", true, []()
                               {
                                   ViewClose(true); // 同步设置
                                   App->Bus()->postpone(AppEvent::FontEvent{});
                               }, "点击应用修改"),
                               EnumItem::New("程序样式", &config_values[KEY_APP_STYLE], 4, nullptr,
                                             [](void*, int index)-> const char*
                                             {
                                                 static constexpr const char* style_names[]
                                                 {
                                                     "Default",
                                                     "Light",
                                                     "Dark",
                                                     "Classic",
                                                 };
                                                 if (index >= 0 && index < std::size(style_names))
                                                 {
                                                     return style_names[index];
                                                 }
                                                 return nullptr;
                                             }, "更改程序样式"),
                               FunctionButton::New("应用##Style", true, []()
                               {
                                   ViewClose(true); // 同步设置
                                   App->Bus()->postpone(AppEvent::StyleEvent{});
                               }, "点击应用修改"),
                               SwitchItem::New("程序启动时窗口默认最大化", &config_values[KEY_WINDOW_MAXIMIZE], "yes"),
                })
        );

        // Statistics
        root->children.emplace_back(
            GroupItem::New("统计功能设置", {
                SwitchItem::New("统计页面图表默认使用饼图而不是柱状图", &config_values[KEY_DEFAUT_PIE_CHART], "yes"),
                })
        );

        // Misc
        //root->children.emplace_back(
        //    GroupItem::New("其他设置", {
        //                       //SwitchItem::New("是否开启垂直同步", &config_values[KEY_ENABLE_V_SYNC], "on"),
        //                       //EnumItem::New("刷新率限制", &config_values[KEY_FRAME_RATE_LIMIT], 5, nullptr,
        //                       //[](void*, int index) -> const char* {
        //                       //    static const char* limits[] { FRAME_RATE_UNLIMIT, FRAME_RATE_LIMIT24, FRAME_RATE_LIMIT30, FRAME_RATE_LIMIT60, FRAME_RATE_LIMIT144 };
        //                       //    if (index >=0 && index < std::size(limits))
        //                       //    {
        //                       //        return limits[index];
        //                       //    }
        //                       //    return nullptr;
        //                       //}, "修改应用程序帧率限制"),
        //        })
        //);

        // Upgrade
        root->children.emplace_back(
            GroupItem::New("在线升级功能设置", {
                TextItem::New("在线升级服务器", &config_values[KEY_UPGRADE_SERVER_URL], false, "检查更新的网站（管理网站）")
                })
        );

        // Extra
        root->children.emplace_back(
            GroupItem::New("扩展功能设置", {
                SwitchItem::New("启用本机调查服务器（Surveyor服务端）", &config_values[KEY_SURVEYOR_SERVER_EN], "on", "", "本机作为surveyor/respondent模式中的surveyor，允许调查连接到本机的respondent。"),
                TextItem::New("远程Surveyor服务端IP地址", &config_values[KEY_SURVEYOR_SERVER_IP], false, "本机作为surveyor/respondent模式中的respondent，可以接受surveyor的调查。"),
            })
        );

        root_item = ConfigItemPtr{ root };
    }

    void ConfigData::LoadToView() const
    {
        if (root_item)
            root_item->Sync(true);
    }

    void ConfigData::SaveToFile() const
    {
        if (root_item)
            root_item->Sync(false);
    }

    ConfigItemPtr GroupItem::New(std::string section_name, const std::initializer_list<ConfigItemPtr>& children,
        bool enabled)
    {
        const auto group_item = new GroupItem;
        group_item->children = children;
        group_item->enabled = enabled;
        group_item->section_name = move(section_name);

        return ConfigItemPtr{ group_item };
    }

    void EditableItemBase::Init(std::string desc, std::string* cfg, std::string tooltip, bool enabled)
    {
        assert(cfg != nullptr && !desc.empty());

        this->description = move(desc);
        this->associate_config = cfg;
        this->tooltip = move(tooltip);
        this->enabled = enabled;
    }

    ConfigItemPtr IntItem::New(std::string desc, std::string* cfg, std::string tooltip, bool enabled)
    {
        auto const item = new IntItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr RealItem::New(std::string desc, std::string* cfg, std::string tooltip, bool enabled)
    {
        auto const item = new RealItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr SwitchItem::New(std::string desc, std::string* cfg, std::string on_text, std::string off_text,
        std::string tooltip, bool enabled)
    {
        assert(!on_text.empty() && on_text != off_text);

        auto const item = new SwitchItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        item->value_text[0] = move(on_text);
        item->value_text[1] = move(off_text);
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr EnumItem::New(std::string desc, std::string* cfg, int count, void* user_data,
        const char* (*item_getter)(void*, int), std::string tooltip, bool enabled)
    {
        assert(count > 0 && item_getter != nullptr);

        auto const item = new EnumItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        item->count = count;
        item->data = user_data;
        item->item_getter = item_getter;
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr TextItem::New(std::string desc, std::string* cfg, bool multi_line, std::string tooltip, bool enabled)
    {
        auto const item = new TextItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        item->multi_line = multi_line;
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr FunctionButton::New(std::string desc, bool same_line, void (*slot)(),
        std::string tooltip, bool enabled)
    {
        assert(slot != nullptr && !desc.empty());

        auto const item = new FunctionButton;
        item->description = std::move(desc);
        item->same_line = same_line;
        item->slot = slot;
        item->tooltip = std::move(tooltip);
        item->enabled = enabled;
        return ConfigItemPtr{ item };
    }

    ConfigItemPtr TimeItem::New(std::string desc, std::string* cfg, std::string tooltip, bool enabled)
    {
        assert(!desc.empty());
        auto const item = new TimeItem;
        item->Init(move(desc), cfg, move(tooltip), enabled);
        return ConfigItemPtr{ item };
    }
}
